package com.webtair.dump4j.core;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import com.webtair.dump4j.algorithm.PropertiesExtractor;
import com.webtair.dump4j.config.ConfigBean;
import com.webtair.dump4j.config.ConfigBeanImpl;
import com.webtair.dump4j.core.impl.DumperImpl;
import com.webtair.dump4j.exception.ConfigurationException;
import com.webtair.dump4j.objectdump.ObjectDump;
import com.webtair.dump4j.producer.Producer;
import com.webtair.dump4j.writer.Writer;

public class DumperFactory {
	static ConfigBean configBean;
	static {
		configBean = new ConfigBeanImpl();
	}

	private DumperFactory(){}
	
	public static void configure(ConfigBean bean){
		configBean = bean;
	}
	
	public static CustomDumper createNewCustomDumper(){
		return createNewCustomDumper(null);
	}
	public static CustomDumper createNewCustomDumper(Class cl) {
		DumperImpl dumper = new DumperImpl();
		initTheDumper(dumper,cl);
		return dumper;
	}

	public static Dumper createNewDumper(){
		return createNewDumper(null);
	}
	public static Dumper createNewDumper(Class cl){
		DumperImpl dumper = new DumperImpl();
		initTheDumper(dumper,cl);
		return dumper;	
	}
	static private void initTheDumper(CustomDumper dumper, Class cl){
		dumper.setDepth(configBean.getDepth());
		dumper.setAutoClean(configBean.getAutoClean());
		
		//instantiate extractor
		String propertyExtractorClassName = configBean.getPropertiesExtractorClassName();
		Class propertyExtractorClass = null;
		try {
			propertyExtractorClass = Class.forName(propertyExtractorClassName);
		} catch (ClassNotFoundException e) {
			throw new ConfigurationException("Class not found: " + propertyExtractorClassName);
		}
		
		if(!PropertiesExtractor.class.isAssignableFrom(propertyExtractorClass)){
			throw new ConfigurationException("Invalid properties extractor class");
		}
		
		PropertiesExtractor extractor = (PropertiesExtractor) create(propertyExtractorClass,cl);
		dumper.setPropertiesExtractor(extractor);
		
		
		// instantiate producer
		String producerClassName = configBean.getProducerClassName();
		Class producerClass = null;
		try {
			producerClass = Class.forName(producerClassName);
		} catch (ClassNotFoundException e) {
			throw new ConfigurationException("Class not found: " + producerClassName);
		}
		
		if(!Producer.class.isAssignableFrom(producerClass)){
			throw new ConfigurationException("Invalid properties producer class");
		}
		
		Producer producer = (Producer) create(producerClass,cl);
		dumper.setProducer(producer);
		
		//instantiate writer
		String writerClassName = configBean.getWriterClassName();
		Class writerClass = null;
		try {
			writerClass = Class.forName(writerClassName);
		} catch (ClassNotFoundException e) {
			throw new ConfigurationException("Class not found: " + writerClassName);
		}
		
		if(!Writer.class.isAssignableFrom(writerClass)){
			throw new ConfigurationException("Invalid properties producer class");
		}
		
		Writer writer = (Writer) create(writerClass,cl);
		dumper.setWriter(writer);
		
		//instantiate object dump;
		String objectDumpClassName = configBean.getObjectDumpClassName();
		Class objectDumpClass = null;
		try {
			objectDumpClass = Class.forName(objectDumpClassName);
		} catch (ClassNotFoundException e) {
			throw new ConfigurationException("Class not found: " + objectDumpClassName);
		}
		
		if(!ObjectDump.class.isAssignableFrom(objectDumpClass)){
			throw new ConfigurationException("Invalid object dump class");
		}
		
		ObjectDump objectDump = (ObjectDump)create(objectDumpClass,cl);
		dumper.setObjectDump(objectDump);
	}
	static private Object create(Class propertyExtractorClass, Class additionalClass) {
		Class[] classArray = new Class[1];
		classArray[0]=Class.class;
		Class[] defaultArray = new Class[0];
		Object[] classArrayObject = new Object[1];
		classArrayObject[0] = additionalClass;
		Object[] defaultArrayObject = new Object[0];
		Constructor constructor = null;
		Object res = null;
		boolean instantiated = false;
		if(additionalClass!=null){
			try {
				constructor = propertyExtractorClass.getConstructor(classArray);
				res = constructor.newInstance(classArrayObject);
				instantiated = true;
			} catch (SecurityException e) {
			} catch (NoSuchMethodException e) {
			} catch (IllegalArgumentException e) {
			} catch (InstantiationException e) {
			} catch (IllegalAccessException e) {
			} catch (InvocationTargetException e) {
			}
		}
		if(!instantiated){
			try {
				constructor = propertyExtractorClass.getConstructor(defaultArray);
				res = constructor.newInstance(defaultArrayObject);
				instantiated = true;
			} catch (SecurityException e1) {
			} catch (NoSuchMethodException e1) {
			} catch (IllegalArgumentException e) {
			} catch (InstantiationException e) {
			} catch (IllegalAccessException e) {
			} catch (InvocationTargetException e) {
			}
		}
		if(!instantiated)
			throw new ConfigurationException("No matching or default constructors found for " +propertyExtractorClass.getName());
		return res;
	}
}
